#pragma once

#include <iostream>
#include <string>
#include <jsoncpp/json/json.h>
#include <memory>
#include <sstream>
#include <signal.h>

#include "compiler.hpp"
#include "run.hpp"
//#include "../common/log.hpp"
#include "../RPC/source/common/log.hpp"
#include "../RPC/source/common/json.hpp"
#include "../common/util.hpp"

namespace oj_compile_and_run
{
    using namespace oj_compiler;
    using namespace oj_runner;
    //using namespace oj_log;
    using namespace oj_util;

    enum class Status
    {
        ERROR_UNKNOW = -1,
        ERROR_CODEISNULL = -2,
        ERROR_COMPILE = -3
    };

    class CompileAndRun
    {
    public:
        /*
         *   in_json:
         *   - code，用户提交的代码
         *   - input，用户的输入（不做处理）
         *   - cpu_limit，cpu限制
         *   - mem_limit，虚拟内存限制
         *
         *   out_json:
         *   必填：
         *   - status，运行状态
         *   - reason，状态描述
         *   选填：
         *   - stdout，运行后输出的结果
         *   - stderr，运行后输出的错误
         */
        static void start(const std::string &in_json, std::string &out_json)
        {
            std::string code;      // 用户提交的代码
            std::string code_file; // 代码源文件名
            int cpu_limit;         // cpu限制
            int mem_limit;         // 虚拟内存限制
            Status status_code;    // 状态
            int run_result;        // 运行结果
            std::string reason;    // 状态描述

            Json::Value out_root;

            // 反序列化
            Json::Value in_root;
            bool ret = JsonMuduoRPC::JSON::unserialize(in_json, in_root);
            if (ret == false)
            {
                status_code = Status::ERROR_UNKNOW; // 反序列化失败(未知错误)
                goto END;
            }

            // 获得用户提交的代码
            code = in_root["code"].asString();
            if (code.size() == 0)
            {
                status_code = Status::ERROR_CODEISNULL;
                goto END;
            }

            // 将代码写入唯一的临时文件
            code_file = FileUtil::uniqueFileName();
            if (!FileUtil::writeFile(PathUtil::Src(code_file), code))
            {
                status_code = Status::ERROR_UNKNOW; // 生成临时文件失败(未知错误)
                goto END;
            }

            // 编译
            if (!Compiler::compile(code_file))
            {
                status_code = Status::ERROR_COMPILE; // 编译失败
                goto END;
            }

            // 运行
            if (in_root["cpu_limit"].isNull() || in_root["mem_limit"].isNull())
            {
                status_code = Status::ERROR_UNKNOW; // 没有设置cpu或内存限制
                goto END;
            }

            cpu_limit = in_root["cpu_limit"].asInt();
            mem_limit = in_root["mem_limit"].asInt();
            run_result = Runner::run(code_file, cpu_limit, mem_limit);
            if (run_result < 0)
                status_code = Status::ERROR_UNKNOW; // 未知错误
            else if (run_result > 0)
                status_code = (Status)run_result;
            else
                status_code = (Status)0;
        END:
            if ((int)status_code == 0)
            {
                std::string stdout_content;
                std::string stderr_content;
                if (!FileUtil::readFile(PathUtil::Stdout(code_file), stdout_content, true) ||
                    !FileUtil::readFile(PathUtil::Stderr(code_file), stderr_content, true))
                {
                    status_code = Status::ERROR_UNKNOW; // 文件打开失败
                }
                else
                {
                    out_root["stdout"] = stdout_content;
                    out_root["stderr"] = stderr_content;
                }
            }

            out_root["status"] = (int)status_code;
            out_root["reason"] = codeToDesc(status_code, code_file);

            // 序列化
            JsonMuduoRPC::JSON::serialize(out_root, out_json);

            //处理完一份代码后，就要将对应的临时文件删除
            removeTempFiles(code_file);
        }

    private:
        static void removeTempFiles(const std::string& file_name)
        {
            FileUtil::removeFile(PathUtil::Src(file_name));
            FileUtil::removeFile(PathUtil::CompileErr(file_name));
            FileUtil::removeFile(PathUtil::Exe(file_name));
            FileUtil::removeFile(PathUtil::Stdin(file_name));
            FileUtil::removeFile(PathUtil::Stdout(file_name));
            FileUtil::removeFile(PathUtil::Stderr(file_name));
        }

        static std::string codeToDesc(const Status code, const std::string &file_name)
        {
            std::string desc;
            switch (code)
            {
            case (Status)0:
                desc = "编译运行成功";
                break;
            case Status::ERROR_UNKNOW:
                desc = "未知错误";
                break;
            case Status::ERROR_CODEISNULL:

                desc = "提交的代码为空";
                break;
            case Status::ERROR_COMPILE:
                FileUtil::readFile(PathUtil::CompileErr(file_name), desc, true);
                break;
            case (Status)SIGXCPU:
                desc = "程序运行超时";
                break;
            case (Status)SIGABRT:
                desc = "使用内存超出限制";
                break;
            case (Status)SIGFPE:
                desc = "浮点数溢出";
                break;
            case (Status)SIGSEGV:
                desc = "段错误";
                break;
            default:
                desc = "unknow: " + std::to_string((int)code);
                break;
            }
            return desc;
        }
    };
}